-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local log = require 'mc.logging'
local context = require 'mc.context'
local client = require 'general_hardware.client'
local skynet = require 'skynet'
local mdb = require 'mc.mdb'

local EVENTS_PATH<const> = '/bmc/kepler/Systems/1/Events'
local EVENTS_INTF<const> = 'bmc.kepler.Systems.Events'

local Event = {}

local function add_event(param)
    local record, event_obj
    client:ForeachEventsObjects(function(o)
        if o.path == EVENTS_PATH then
            event_obj = o
        end
    end)
    if not event_obj then
        log:error('[general_hardware]get events object failed')
        return false
    end

    local ok, res = pcall(function ()
        record = event_obj:AddEvent_PACKED(context.new(), param):unpack()
    end)
    if not ok then
        error(string.format('[general_hardware]generate event fail, %s', res))
    end
    log:info('[general_hardware]add event(%s) successfully', record)
    return true
end

local RETRY_TIMES = 10
local function retry_add_event(param)
    local ok, ret
    for _ = 1, RETRY_TIMES do
        ok, ret = pcall(add_event, param)
        if ok and ret then
            return true
        end
        skynet.sleep(50)
    end
    return false, ret
end

--- 产生软件告警
function Event.generate_event(msg)
    local res, ret = retry_add_event(msg)
    if not res then
        error(string.format('[general_hardware]generate event fail, %s', ret))
    end
end

local function format_event_list(list)
    if #list == 0 then
        return {}
    end
    -- 计算结构体参数个数num，接口返回的list为 sel * num 个数组,无法通过key直接获取sel对象，同时一个对象的参数个数不固定
    local num = 0
    local end_flag = list[1].MappingTable[1].Key -- 以第一个Key为标记，重复时说明遍历到第二个对象
    for key, value in pairs(list) do
        if value.MappingTable[1].Key == end_flag and key ~= 1 then
            break
        end
        num = num + 1
    end
    local count = 0
    local res = {}
    local event = {}
    for _, v in pairs(list) do
        event[v.MappingTable[1].Key] = v.MappingTable[1].Value
        count = count + 1
        if count % num == 0 then -- 每num个参数组成一个对象
            local temp = event
            event = {}
            res[#res + 1] = temp
        end
    end
    return res
end

-- 获取最新当前告警列表
function Event.get_latest_alarm_list(bus)
    if not Event.event_obj then
        local ok, event_obj = pcall(mdb.get_object, bus, EVENTS_PATH, EVENTS_INTF)
        if not ok then
            log:error('[get_latest_alarm_list] get Events object fail, err: %s', event_obj)
            return ok, nil
        end
        Event.event_obj = event_obj
    end
    -- 0xffff 表示查询条数最大
    local ok, ret = Event.event_obj.pcall:GetAlarmList_PACKED(context.new(), 1, 0xffff, {})
    if not ok then
        log:error('[get_latest_alarm_list] get event list fail, error: %s', ret)
        return ok, nil
    end
    if not ret.EventList then
        log:error('[get_latest_alarm_list] prop EventList is nil')
        return false, nil
    end
    return true, format_event_list(ret.EventList)
end

return Event